package com.tianji.learning.service.impl;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.course.SectionInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningLessonDTO;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.service.ILearningRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.tianji.learning.enums.LessonStatus.*;
import static com.tianji.learning.enums.PlanStatus.NO_PLAN;
import static com.tianji.learning.enums.PlanStatus.PLAN_RUNNING;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author 陈宽
 * @since 2025-06-27
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {
    private final CourseClient courseClient;
    private final LearningRecordMapper recordMapper;
    private final CatalogueClient catalogueClient;

    @Override
    public void addLessonsPlan(LearningPlanDTO dto) {
        //根据dto查询对应的课表
        LearningLesson lesson = this.lambdaQuery().eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getCourseId, dto.getCourseId()).one();
        AssertUtils.isNotNull(lesson, "该课程不存在");

        boolean update = this.lambdaUpdate()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .set(LearningLesson::getWeekFreq, dto.getFreq())
                .set(lesson.getPlanStatus() == NO_PLAN, LearningLesson::getPlanStatus, PLAN_RUNNING)
                .eq(LearningLesson::getCourseId, dto.getCourseId())
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .update();
        if (!update) {
            throw new RuntimeException("添加课程计划失败");
        }
    }

    @Override
    public LearningPlanPageVO queryLearningPlans(PageQuery query) {
        LearningPlanPageVO pageVO = new LearningPlanPageVO();
        //从课程表里面拿到latest_learn_time ‘最近一次学习的时间'，
        //latest_learn_time ‘最近一次学习的时间'，
        Page<LearningLesson> learningLessonPage = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getPlanStatus, PLAN_RUNNING)
                .in(LearningLesson::getStatus, NOT_BEGIN, LEARNING, FINISHED)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = learningLessonPage.getRecords();
        if (CollUtils.isEmpty(records)) {
            return pageVO.emptyPage(learningLessonPage);
        }

        // 3、收集课程ID，根据课程ID集合远程调用查询课程信息
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = getCourseInfoMap(records);
        //分组来获得课程的map（课程的id，完成的数量）
        // 4.1、获取本周开始、结束日期
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());

        // 4.2、发起分组统计
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("lesson_id", "count(1) num")
                .eq("user_id", UserContext.getUser())
                .eq("finished", true)
                .between("finish_time", begin, end)
                .groupBy("lesson_id");
        //本周每门课程的完成数
        List<Map<String, Object>> mapList = this.recordMapper.selectMaps(queryWrapper);
        Map<Long, Long> learnedSectionNumMap = mapList.stream().collect(Collectors.toMap(
                m -> (Long) m.get("lesson_id"), m -> (Long) m.get("num")
        ));
        List<LearningPlanVO> learningPlanVOList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            LearningPlanVO vo = BeanUtils.copyBean(record, LearningPlanVO.class);
            // 设置课程信息
            if (courseInfoMap.get(record.getCourseId()) != null) {
                vo.setCourseName(courseInfoMap.get(record.getCourseId()).getName());
                vo.setSections(courseInfoMap.get(record.getCourseId()).getSectionNum());
            }
            //weekLearnedSections本周已学习数量
            vo.setWeekLearnedSections(learnedSectionNumMap.getOrDefault(record.getId(), 0L).intValue());
            learningPlanVOList.add(vo);
        }
        // 6、计算 本周已学习小节总数量
        long sum = learnedSectionNumMap.values().stream().mapToLong(Long::longValue).sum();

        // 7、计算 本周计划学习的总小节数
        // SELECT sum(week_freq) total_weekfreq FROM learning_lesson WHERE user_id = 2 AND plan_status = 1 AND `status` IN (0,1,2)
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", UserContext.getUser())
                .eq("plan_status", PLAN_RUNNING)
                .in("status", NOT_BEGIN, LEARNING, FINISHED).one();

        // 8、封装数据并返回
        pageVO.setWeekFinished(Long.valueOf(sum).intValue());
        pageVO.setWeekTotalPlan(lesson.getTotalWeekfreq());
        pageVO.pageInfo(learningLessonPage.getTotal(), learningLessonPage.getPages(), learningPlanVOList);
        return pageVO;
    }

    @Override
    public void addLessons(Long userId, List<Long> courseIds) {
        log.info("用户{} ID为：{} 的课程已支付成功，开始添加课程到课表", userId, courseIds);
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        if (simpleInfoList == null) {
            throw new RuntimeException("课程信息为空，请检查课程ID是否正确");
        }
        List<LearningLesson> lessons = new ArrayList<>(simpleInfoList.size());
        for (CourseSimpleInfoDTO info : simpleInfoList) {
            LearningLesson lesson = new LearningLesson();
            lesson.setUserId(userId);
            lesson.setCourseId(info.getId());


            // 计算课程有效期并填充
            Integer validDuration = info.getValidDuration();
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            if (validDuration < 9999) {
                // LocalDateTimeUtil.offset(LocalDateTime.now(), courseInfo.getValidDuration(), ChronoUnit.MONTHS);
                lesson.setExpireTime(LocalDateTime.now().plusMonths(validDuration));
            }
            lessons.add(lesson);
        }
        boolean saveBatch = saveBatch(lessons);
        if (!saveBatch) {
            throw new RuntimeException("添加课程失败");
        }
    }

    @Override
    public void deleteCourseFromLesson(Long userId, List<Long> courseIds) {
        boolean remove = remove(new LambdaQueryWrapper<LearningLesson>()
                .eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getCourseId, courseIds)
        );
        if (!remove) {
            log.error("删除用户课程失败，用户id：{}，课程id：{}", userId, courseIds);
            throw new RuntimeException("删除失败");
        }
    }

    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        /**
         * "list":[
         *     {
         *       "id": 0, // 课表id
         *       "courseId": 0, // 课程id
         *       "courseName": "", // 课程名称 要远程调用
         *       "courseCoverUrl": "", // 课程封面 要远程调用
         *       "createTime": "", // 加入课表时间
         *       "expireTime": "", // 过期时间
         *       "learnedSections": 0, // 已经学习小节数
         *       "sections": 0, // 总小节数 远程调用查询
         *       "status": "", // 课程状态
         *       "weekFreq": 0 // 学习计划频率
         *       "planStatus": "", // 学习计划状态
         *     }
         */
        //获得当前用户的id
        Long userId = UserContext.getUser();
        //分页查询课程表数据
        Page<LearningLesson> latestLearnTime = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .page(query.toMpPage("latest_learn_time", false));
        List<Long> courseIds = new ArrayList<>();
        latestLearnTime.getRecords().forEach(item -> {
            courseIds.add(item.getCourseId());
        });
        List<LearningLessonVO> VOlist = new ArrayList<>();
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        Map<Long, CourseSimpleInfoDTO> collect = simpleInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        for (LearningLesson record : latestLearnTime.getRecords()) {
            LearningLessonVO VO = BeanUtils.toBean(record, LearningLessonVO.class);
            VO.setCourseName(collect.get(record.getCourseId()).getName());
            VO.setCourseCoverUrl(collect.get(record.getCourseId()).getCoverUrl());
            VO.setSections(collect.get(record.getCourseId()).getSectionNum());
            VOlist.add(VO);
        }
        return PageDTO.of(latestLearnTime.getTotal(), latestLearnTime.getPages(), VOlist);
    }

    @Override
    public LearningLessonVO learingNowLesson() {
        //1.获得当前用户的id
        Long userId = UserContext.getUser();
        //2.通过用户id查询当前正在学习的课程，通过最近学习的时间来倒序，limit 1
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("LIMIT 1")
                .one();
        if (ObjectUtil.isEmpty( lesson)){
            return null;
        }
        //3.通过课程id和用户id查询课程的name和sections
        CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (ObjectUtil.isEmpty(courseInfoById)) {
            throw new BizIllegalException("课程信息不存在，无法查询课表");
        }
        //4.计算课表里面的课程数量
        Integer count = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();
        //5.通过latest_section_id来远程调用
        // "List<CataSimpleInfoDTO> cataInfos =catalogueClient.batchQueryCatalogue"
        // 来查询当前正在学习的小节名称和索引
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
        CataSimpleInfoDTO cataSimpleInfoDTO = cataSimpleInfoDTOS.get(0);
        //6.数据收集齐全，组装数据并返回
        LearningLessonVO vo = BeanUtils.toBean(lesson, LearningLessonVO.class);
        vo.setCourseName(courseInfoById.getName());
        vo.setSections(courseInfoById.getSectionNum());
        vo.setCourseCoverUrl(courseInfoById.getCoverUrl());
        vo.setLatestSectionName(cataSimpleInfoDTO.getName());
        vo.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());
        vo.setCourseAmount(count);
        return vo;
    }

    @Override
    public Long isLessonValid(Long courseId) {
        //1.获得当前用户的id
        Long userId = UserContext.getUser();
        //2.通过用户id和课程id查询课程表数据
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        AssertUtils.isNotNull( lesson, "课程不存在");
        //3.通过用户课表中是否有该课程和课程状态是否是有效的状态（未过期）来判断课程表数据是否有效
        if (lesson != null && !(lesson.getStatus()==EXPIRED)) {
            return lesson.getId();
        }
        //有效就返回课程id，无效就返回null
        return null;
    }

    @Override
    public LearningLessonVO queryCourseStatus(Long courseId) {
        //1.获得用户的id
        Long userId = UserContext.getUser();
        //2.通过课程ID和用户ID查询课程信息
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null){
            return null;
        }
        //3.封装成VO返回
        return BeanUtils.toBean(lesson, LearningLessonVO.class);
    }

    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        /**
         * NOT_BEGIN(0, "未学习"),
         *     LEARNING(1, "学习中"),
         *     FINISHED
         */
        Integer count = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus, NOT_BEGIN, LEARNING, FINISHED)
                .count();
        return count;
    }

    private Map<Long, CourseSimpleInfoDTO> getCourseInfoMap(List<LearningLesson> records) {
        // 1、获取课程ID集合
        // Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        Set<Long> courseIds = CollStreamUtil.toSet(records, LearningLesson::getCourseId);

        // 2、发起远程调用，查询课程详细信息（课程名称、封面、总章节数）
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);

        // 3、将课程信息转为Map
        return CollStreamUtil.toMap(courseInfoList, CourseSimpleInfoDTO::getId, Function.identity());
    }
}
